/*
 * @Description: 路由前置守卫
 * @Version: 1.0
 * @Autor: chen_yt
 * @Date: 2022-03-09 14:34:47
 * @LastEditors: wei_jt@ecidi.com
 * @LastEditTime: 2022-05-07 18:19:29
 */
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import store from '@/store'
import { localRoute } from '@/config'
import { getLocalRoutes, getUserRoutes } from '@/utils/route'
import { checkPwdStatus } from '@/utils/password'
import storage from '@/utils/storage'
import Vue from 'vue'


NProgress.configure({ showSpinner: false }) // NProgress Configuration



export class GuardsList {
  constructor() {
    this.strateges = {
      'normal': (to, from, next) => { return new normalBeforeGuards(to, from, next) },
      'mainapp': (to, from, next) => { return new mainappBeforeGuards(to, from, next) },
      'subapp': (to, from, next) => { return new subappBeforeGuards(to, from, next) },
      'inqiankun': (to, from, next) => { return new inqiankunBeforeGuards(to, from, next) },
    }
  }
  getGuard (name, to, from, next) {
    return this.strateges[name].call(null, to, from, next)
  }

}

/**
 * @description: 路由守卫基类
 *               包括基础跳转及动态加载路由
 *               
 */
class normalBeforeGuards {
  constructor(to, from, next) {
    this.to = to
    this.from = from
    this.next = next
  }
  /**
     * @description: 对路由进行重定向
     *               添加next()钩子后返回true，否则返回false
     * @return {Boolean} result
     */
  routeRedirect () {
    return false
  }

  /**
    * @description: routeRedirect未添加next()钩子时
    *               需要调用该方法解析路由
    */
  routeResolve () {
    this.routeMatch().then(res => {
      if (!res) {
        this.next('/403')
      }
    })
  }

  /**
     * @description: 对路由进行解析
     *               添加next()钩子后返回Promise.resolve(true)，否则返回Promise.resolve(false)              
     * @return {Promise} 
     */
  async routeMatch () {

    //全屏路由匹配成功时可直接放过
    if (this.to.matched?.length > 0 && this.to.meta?.config?.includes('fullScreen')) {
      this.next()
      return true
    }

    storage.set('redirectUrl', this.to.fullPath)

    //已加载路由的情况
    if (store.state.routes.length > 0) {
      if (this.to.matched?.length > 0) {
        this.next()
        return true
      }
      return false
    }

    //加载本地路由
    if (localRoute) {
      let result = await getLocalRoutes()
      if (!result) {
        return false
      }
      this.next({ ...this.to, replace: true })
      return true
    }

    //加载线上路由
    let result = await getUserRoutes()
    if (!result) {
      return false
    }

    //校验密码
    let status = await checkPwdStatus()
    if (status) {
      this.next({ path: status, replace: true })
      return true
    }

    this.next({ ...this.to, replace: true })
    return true
  }
}

/**
 * @description: 主应用中
 *               发现路由query参数带有keepOrigin时去除
 * 
 */
class mainappBeforeGuards extends normalBeforeGuards {
  routeRedirect () {
    if (this.to.query?.keepOrigin) {
      let query = this.to.query
      delete query.keepOrigin
      this.next({ path: this.to.path, query: query, params: this.to.params, meta: this.to.meta, replace: true })
      return true
    }
    return super.routeRedirect()
  }
}

/**
 * @description: 子应用模式下 
 *               有keepOringin参数时，重定向到不含/sub_app_xx前缀的路由
 *               无keepOringin参数时，重定向到含有/sub_app_xx前缀的路由
 */
class subappBeforeGuards extends normalBeforeGuards {
  routeRedirect () {
    let prefix = Vue.prototype.$appBasePath

    if (this.to.meta?.keepOrigin || this.to.query?.keepOrigin) {
      if (this.to.path.includes(prefix)) {
        this.next({ path: this.to.path.replace(prefix, ''), query: this.to.query, params: this.to.params, meta: this.to.meta, replace: true })
        return true
      }
      else {
        return super.routeRedirect()
      }
    }

    if (!this.to.path.includes(prefix)) {
      this.next({ path: prefix + this.to.path, query: this.to.query, params: this.to.params, meta: this.to.meta, replace: true })
      return true
    }

    return super.routeRedirect()
  }
}

/**
 * @description: qiankun环境下
 *               路由匹配失败时，去掉/sub_app_xx前缀，让主应用来处理这个路由
 */
class inqiankunBeforeGuards extends subappBeforeGuards {
  routeRedirect () {
    return super.routeRedirect()
  }
  routeResolve () {
    this.routeMatch().then(res => {
      if (!res) {
        NProgress.done()
        let prefix = Vue.prototype.$appBasePath
        if (this.to.path.includes(prefix)) {
          let path = this.to.path.replace(prefix, '')
          this.next({ path: path, query: { ...this.to.query, keepOrigin: true }, params: this.to.params, meta: this.to.meta, replace: true })
        }
        else {
          this.next()
        }
      }
    })
  }
}
